package com.winforsys.yellowbook;

import java.util.ArrayList;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.os.AsyncTask;
import android.widget.AdapterView.OnItemClickListener;
import com.google.android.maps.GeoPoint;
import com.google.android.maps.MapView;
import com.google.android.maps.Overlay;
import com.google.android.maps.Projection;

public class MarkerOverlay extends Overlay {

	private ArrayList<Business> mOverlays = new ArrayList<Business>();
	private Bitmap mBitmapMarker;
	private Bitmap mBitmapShadow;
	DropMarkersTask animateMarkers;
	OnItemClickListener itemClickedListener;

	public MarkerOverlay(Bitmap defaultMarker) {
		
		mBitmapMarker = defaultMarker;
		
		// Create shadow bitmap. Basically a black version of the image
		mBitmapShadow = defaultMarker.copy(Bitmap.Config.ARGB_8888, true);
		
		for (int x = 0; x < mBitmapShadow.getWidth(); x++)
			for (int y = 0; y < mBitmapShadow.getHeight(); y++)
				if (mBitmapShadow.getPixel(x, y) != Color.TRANSPARENT)

					// This is a little lazy but it works
					mBitmapShadow.setPixel(x, y, Color.BLACK);
	}

	public void setOnItemClickListener(OnItemClickListener clickListener) {
		this.itemClickedListener = clickListener;
	}

	public void addBusiness(Business overlay) {
		
		for (Business business : mOverlays) {
			
			if (overlay.getPoint().getLatitudeE6() == business.getPoint()
					.getLatitudeE6()
					&& overlay.getPoint().getLongitudeE6() == overlay
							.getPoint().getLongitudeE6()) {
				
				// Don't add any markers which exist at exactly the same
				// location, chances are it's the same marker
				return;
				
			} else if (overlay.getPoint().getLatitudeE6() > business.getPoint()
					.getLatitudeE6()) {
				
				// This is so all the markers are listed top to bottom
				mOverlays.add(mOverlays.indexOf(business), overlay);
				return;
			}
		}
		
		mOverlays.add(overlay);
	}

	public Business getItem(int position) {
		
		return mOverlays.get(position);
	}

	@Override
	public void draw(Canvas canvas, MapView mapView, boolean shadow) {
		
		super.draw(canvas, mapView, shadow);
		Projection projection = mapView.getProjection();
		boolean animationRequired = false;
		
		// Find the bounds in which the markers must reside to be displayed
		GeoPoint bottomLeft = projection.fromPixels(
				-mBitmapMarker.getWidth() / 2, mapView.getHeight()
						+ mBitmapMarker.getHeight());
		GeoPoint topRight = projection.fromPixels(mapView.getWidth()
				+ mBitmapMarker.getWidth() / 2, 0);
		
		for (Business business : mOverlays) {
			// Check to ensure the marker is inside the bounds
			if (business.getPoint().getLatitudeE6() > bottomLeft
					.getLatitudeE6()
					&& business.getPoint().getLatitudeE6() < topRight
							.getLatitudeE6()
					&& business.getPoint().getLongitudeE6() > bottomLeft
							.getLongitudeE6()
					&& business.getPoint().getLongitudeE6() < topRight
							.getLongitudeE6()) {
				if (business.isNewPoint()) {
					business.setOffset(mapView.getHeight());
					business.setOldPoint();
				}
				Point pt = new Point();
				projection.toPixels(business.getPoint(), pt);
//				if (shadow) {
//					// Set the location of the shadow according to the offset so
//					// it appears to come in from the top right
//					pt.x = pt.x + (mBitmapMarker.getWidth() / 4)
//							+ ((int) business.getOffset() / 2);
//					pt.y = pt.y - (mBitmapMarker.getHeight() / 2)
//							- ((int) business.getOffset() / 2);
//
//					// Skew the shadow and set the location
//					Matrix matrix = new Matrix();
//					matrix.preSkew(-0.8f, 0f);
//					matrix.preScale(1f, 0.5f);
//					matrix.postTranslate(pt.x, pt.y);
//					// Change transparency according to the offset
//					Paint paint = new Paint();
//					paint.setAlpha((int) (((mapView.getHeight() - business
//							.getOffset()) / mapView.getHeight()) * 100));
//
//					// Draw it
//					canvas.drawBitmap(mBitmapShadow, matrix, paint);
//				} else {
					// Set the position according to the offset
					pt.x = pt.x - (mBitmapMarker.getWidth() / 2);
					pt.y = pt.y - mBitmapMarker.getHeight()
							- (int) business.getOffset();
					canvas.drawBitmap(mBitmapMarker, (float) pt.x,
							(float) pt.y, null);
					if (business.getOffset() > 0) {
						animationRequired = true;
					}
				//}
			}
		}

		// Start the animation task if it hasn't already been started
		if (animationRequired
				&& (animateMarkers == null || animateMarkers.getStatus() != AsyncTask.Status.RUNNING)) {
			animateMarkers = new DropMarkersTask();
			animateMarkers.execute(mapView);
		}
	}

	@Override
	public boolean onTap(GeoPoint point, MapView map) {
		if (itemClickedListener == null) {
			return false;
		}

		Projection projection = map.getProjection();
		int imageWidth = mBitmapMarker.getWidth();
		int imageHeight = mBitmapMarker.getHeight();

		// Find the point on the screen which has been clicked

		Point clickPoint = new Point();
		projection.toPixels(point, clickPoint);

		// Go backwards through the businesses and find out if the location
		// falls within their marker
		for (int i = mOverlays.size() - 1; i >= 0; i--) {
			Business business = mOverlays.get(i);

			Point businessPoint = new Point();

			projection.toPixels(business.getPoint(), businessPoint);

			if (businessPoint.x > 0 && businessPoint.x < map.getWidth() &&

			businessPoint.y > 0 && businessPoint.y < map.getHeight()) {

				// Point is visible, so may clicked
				int left = businessPoint.x - (imageWidth / 2);

				int right = businessPoint.x + (imageWidth / 2);

				int top = businessPoint.y - imageHeight;
				int bottom = businessPoint.y;

				if (clickPoint.x >= left && clickPoint.x <= right
						&& clickPoint.y >= top && clickPoint.y <= bottom) {

					// Item has been clicked
					// Adapter will be null as this isn't one. We will return
					// the map
					// in the view for consistency but most importantly the
					// index of the item
					itemClickedListener.onItemClick(null, map, i, 0);
					return true;
				}
			}
		}

		return false;
	}

	class DropMarkersTask extends AsyncTask<MapView, Void, Void> {
		MapView mapView;

		@Override
		protected Void doInBackground(MapView... mapViews) {
			mapView = mapViews[0];
			boolean mapUpdate = true;

			try {
				while (mapUpdate) {
					Projection projection = mapView.getProjection();
					GeoPoint bottomLeft = projection.fromPixels(
							-mBitmapMarker.getWidth() / 2, mapView.getHeight()
									+ mBitmapMarker.getHeight());
					GeoPoint topRight = projection.fromPixels(
							mapView.getWidth() + mBitmapMarker.getWidth() / 2,
							0);
					mapUpdate = false;

					// Any visible markers with an offset higher than zero must
					// be falling and therefore must be moved.

					for (Business business : mOverlays) {
						if (business.getPoint().getLatitudeE6() > bottomLeft
								.getLatitudeE6()
								&& business.getPoint().getLatitudeE6() < topRight
										.getLatitudeE6()
								&& business.getPoint().getLongitudeE6() > bottomLeft
										.getLongitudeE6()
								&& business.getPoint().getLongitudeE6() < topRight
										.getLongitudeE6()) {
							if (business.getOffset() > 0) {
								// A nice Quadratic fall curve.
								double currentY = Math.sqrt(mapView.getHeight()
										- business.getOffset());
								currentY = currentY + 0.5;
								double dropDistance = Math.pow(currentY, 2);
								double newOffset = mapView.getHeight()
										- dropDistance;
								if (newOffset < 0) {
									// Marker can't have an offset less than
									// zero
									newOffset = 0;
								}

								business.setOffset(newOffset);
								mapUpdate = true;
							}
						}
					}

					if (mapUpdate) {
						this.publishProgress();
						Thread.sleep(20);
					}
				}
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return null;
		}

		@Override
		protected void onProgressUpdate(Void... unused) {
			mapView.postInvalidate();
		}
	}
}